package com.example.service.impl;

import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.constant.PromotionConstants;
import com.example.dto.OrderCreateDTO;
import com.example.dto.UserCouponDTO;
import com.example.entity.Coupon;
import com.example.entity.Order;
import com.example.entity.UserCoupon;
import com.example.entity.enums.UserCouponStatusEnum;
import com.example.event.UserReceiveCouponMessageEvent;
import com.example.event.publisher.EventPublisher;
import com.example.mapper.CouponMapper;
import com.example.mapper.UserCouponMapper;
import com.example.service.UserCouponService;
import com.example.util.BeanCopyPropertiesUtils;
import com.example.util.BeanUtils;
import com.example.util.CollUtils;
import com.example.util.RedisUtil;
import com.example.vo.LoginUser;
import com.example.vo.Result;
import com.example.vo.ResultCode;
import com.example.vo.UserCouponVO;
import com.example.vo.buyer.CartCouponVo;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author circle48
* @description 针对表【user_coupon(用户优惠券关联表)】的数据库操作Service实现
* @createDate 2024-05-17 14:37:16
*/
@Service
@Slf4j
public class UserCouponServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon>
    implements UserCouponService{

    @Resource
    private UserCouponMapper userCouponMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private EventPublisher eventPublisher;
    @Resource
    private UserReceiveCouponMessageEvent userReceiveCouponMessageEvent;


    @Resource
    private RedisUtil redisUtil;

    @Resource
    private CouponMapper couponMapper;



    @Override
    public Result getMemberCoupons(Integer pageNum, Integer pageSize, String couponStatus, Long id) {
        Page<UserCouponVO> page = new Page<>(pageNum, pageSize);
        IPage<UserCouponVO> userCouponPage = userCouponMapper.selectUserCoupons(page, id, couponStatus);
        return Result.ok(userCouponPage);
    }

    @Override
    public Result getCouponsByStore(Long userId, Long storeId){
        List<Coupon> coupons = couponMapper.selectUserCouponsByStore(storeId);
        LocalDateTime now = LocalDateTime.now();

        // 过滤并转换优惠券列表
        List<CartCouponVo> validCoupons = coupons.stream()
                .filter(coupon -> {
                    // 检查用户是否已领取优惠券
                    boolean isCollected = userCouponMapper.isCouponCollected(userId, coupon.getId());
                    // 如果已领取或在可领取时间内，则保留该优惠券
                    return isCollected || (now.isAfter(coupon.getEnableStartTime()) && now.isBefore(coupon.getEnableEndTime()));
                })
                .map(coupon -> {
                    // 创建 CartCouponVo 对象
                    CartCouponVo vo = new CartCouponVo();
                    vo.setId(coupon.getId());
                    vo.setPromotionName(coupon.getPromotionName());
                    vo.setReduceAmount(coupon.getReduceAmount());
                    vo.setDescription(coupon.getDescription());
                    // 设置领取状态
                    vo.setStatus(userCouponMapper.isCouponCollected(userId, coupon.getId()));
                    vo.setCouponCategory(coupon.getCouponCategory());
                    return vo;
                })
                .collect(Collectors.toList());

        return Result.ok(validCoupons);
    }

    @Override
    public Result receiveBuyerCoupon(Long couponId, Long userId) {
        // 1.查询优惠券
        Coupon coupon = queryCouponByCache(couponId);
        if (coupon == null) {
            return Result.error(ResultCode.COUPON_NOT_FOUND);
        }
        // 2.校验发放时间
        LocalDateTime now = LocalDateTime.now();
        if (now.isBefore(coupon.getEnableStartTime()) || now.isAfter(coupon.getEnableEndTime())) {
            return Result.error(ResultCode.COUPON_CAMPAIGN_INACTIVE);
        }
        // 3.校验库存
        if (coupon.getReceiveCount() >= coupon.getPublishCount()) {
            return Result.error(ResultCode.COUPON_OUT_OF_STOCK);
        }

        // 4.校验每人限领数量
        // 4.1.查询领取数量
        String key = PromotionConstants.USER_COUPON_CACHE_KEY_PREFIX + couponId;
        Long count = redisUtil.hashIncrement(key, userId.toString(), 1);


        // 4.2.校验限领数量
        if(count > coupon.getLimitReceiveCount()){
            return Result.error(ResultCode. COUPON_NOT_REPEATABLE);
        }
        // 5.扣减优惠券库存
        //Long totalNum = redisUtil.hashIncrement(PromotionConstants.COUPON_CACHE_KEY_PREFIX + couponId, "publishCount", -1);

        Long currentReceiveCount = stringRedisTemplate.opsForHash().increment(PromotionConstants.COUPON_CACHE_KEY_PREFIX +couponId, "receiveCount", +1L);

        // 6. 发送消息到MQ
        if (currentReceiveCount <= coupon.getPublishCount()) {
            UserCouponDTO dto = new UserCouponDTO();
            dto.setCouponId(couponId);
            dto.setUserId(userId);
            sendUserReceiveCouponMessage(dto);
        } else {
            // 如果尝试领取导致数量超过限制，需要回滚领取计数并处理错误情况
            stringRedisTemplate.opsForHash().increment(
                    PromotionConstants.COUPON_CACHE_KEY_PREFIX + couponId, "receiveCount", -1L);
            return Result.error(ResultCode.COUPON_OUT_OF_STOCK);
        }
        return Result.ok("领取成功");
    }

    public void sendUserReceiveCouponMessage(UserCouponDTO dto) {
        log.info("发送优惠券消息到MQ");
        eventPublisher.publish(userReceiveCouponMessageEvent.topic(), userReceiveCouponMessageEvent.routingKey(), userReceiveCouponMessageEvent.buildEventMessage(dto),0);
    }


    @Override
    public void checkAndCreateUserCoupon(Coupon coupon, Long userId) {
        // 1. 优惠券的领取数量+1   UPDATE coupon set issue_num  =  issue_num+1 WHERE id =?
        //int rows = couponMapper.incrIssueNum(coupon.getId(),coupon.getIssueNum());
        // 将更新后的领取计数同步到数据库
        int rows = couponMapper.incrReceiveCount(coupon.getId(),coupon.getPublishCount());
        if (rows == 0) {
            throw new RuntimeException("优惠券领取失败");
        }

        // 2. 生成用户券
        UserCoupon userCoupon = BeanCopyPropertiesUtils.copyBean(coupon,UserCoupon.class);
        userCoupon.setUserId(userId);
        userCoupon.setCouponId(coupon.getId());
        //封装优惠券使用的期限时间
        LocalDateTime termEndTime = coupon.getEndTime();
        userCoupon.setReceiveTime(LocalDateTime.now());
        userCoupon.setEndTime(termEndTime);

        save(userCoupon);


    }

    private Coupon queryCouponByCache(Long id) {
        String key = PromotionConstants.COUPON_CACHE_KEY_PREFIX + id;

        // 取出hash里面的hashkey和hashvalue
        Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(key);

        if (CollUtils.isEmpty(map)) {
            return null;
        }

        //2.转成Coupon对象
        // 将哈希表数据拷贝到Coupon对象中
        Coupon coupon = BeanUtils.mapToBean(map, Coupon.class, false, CopyOptions.create());
        coupon.setId(id);
        return coupon;

    }

    @Override
    public List<UserCoupon> queryMyCoupon() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long id = loginUser.getUser().getId();
        LambdaQueryWrapper<UserCoupon> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserCoupon::getUserId, id);
        queryWrapper.eq(UserCoupon::getStatus, UserCouponStatusEnum.UNUSED.getValue());
//        queryWrapper.in(UserCoupon::getCouponId, couponIds);
//        queryWrapper.ne(UserCoupon::getScopeType, PromotionsScopeTypeEnum.ALL.name());
//        queryWrapper.le(UserCoupon::getMinPoint, totalPrice);
        queryWrapper.ge(UserCoupon::getEndTime, new Date());
        return this.list(queryWrapper);
    }


    @Override
    public UserCouponVO getCouponsById(Long couponId) {
        UserCoupon userCoupon = baseMapper.selectById(couponId);
        if (userCoupon == null) {
// 处理null值的情况，例如返回错误信息或null对象
            throw new IllegalArgumentException("No coupon found with id: " + couponId);
        }
        return BeanCopyPropertiesUtils.copyBean(userCoupon, UserCouponVO.class);
    }


    @Override
    public List<UserCouponVO> getCouponsByIds(List<Long> couponIds) {
        return baseMapper.getCouponsByIds(couponIds);
    }


    @Override
    public void updateCouponStatusToUsed(Long couponId, Long orderId, String orderSn) {
        baseMapper.updateCouponStatusToUsed(couponId,orderId,orderSn);
    }


    @Override
    public void updateCouponStatusToUnUsed(List<OrderCreateDTO> orders) {
        Set<Long> processedCouponIds = new HashSet<>(); // 用于存储已处理过的优惠券ID

        for (OrderCreateDTO orderCreateDTO : orders) {
            Order order = orderCreateDTO.getOrder();
            String couponIds = order.getCouponIds();
            if (couponIds != null && !couponIds.isEmpty()) {
                // 分割 couponIds 字符串并遍历每个优惠券ID
                String[] couponIdArray = couponIds.split(",");
                for (String couponIdStr : couponIdArray) {
                    try {
                        Long couponId = Long.parseLong(couponIdStr);
                        // 如果已经处理过该优惠券ID，则跳过
                        if (processedCouponIds.contains(couponId)) {
                            continue;
                        }
                        // 更新每个优惠券状态为未使用状态
                        baseMapper.updateCouponStatusToUnUsed(couponId);
                        // 将优惠券ID加入已处理集合
                        processedCouponIds.add(couponId);
                    } catch (NumberFormatException e) {
                        log.error("无效的优惠券ID: {}", couponIdStr, e);
                    }
                }
            }
        }
    }



    @Override
    public void updateExpiredUserCoupons(LocalDateTime currentTime) {
        List<UserCoupon> userCouponList = lambdaQuery()
                .eq(UserCoupon::getStatus, UserCouponStatusEnum.UNUSED)  // Assuming UNUSED is the code for 未使用
                .le(UserCoupon::getEndTime, currentTime)
                .list();

        userCouponList.forEach(userCoupon -> {
            userCoupon.setStatus(UserCouponStatusEnum.EXPIRED); // Assuming EXPIRED is the enum for 已过期
            updateById(userCoupon);
        });
    }
}




